package com.zui.webapp.action;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import javax.servlet.http.Cookie;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetails;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
import com.zui.entity.BaseEntity;
import com.zui.entity.party.Party;
import com.zui.entity.product.Geo;
import com.zui.entity.trade.Trade;
import com.zui.manager.GeoManager;
import com.zui.manager.UserManager;
import com.zui.security.Role;
import com.zui.security.User;
import com.zui.util.JqGridPage;
import com.zui.util.RandomStringGenerator;
import com.zui.util.ZuiConstants;
import com.zui.util.mail.MailManager;

public class UserAction extends BaseJQGridAction {

	private static final long serialVersionUID = 1L;
	public UserAction() {}
	public UserAction(UserManager userManager) {
		this.userManager = userManager;
	}
	private User user;

	private List<User> userList = new ArrayList();
	private List roleList = new ArrayList();
	private List selectRoleList = new ArrayList();
	
	
	private Geo geo;
	private List<Geo> areaList;
	
	@Autowired
	private UserManager userManager;
	@Autowired
	private Md5PasswordEncoder md5PasswordEncoder;
	@Autowired
	private MailManager mailManager;
	@Autowired
	@Qualifier("org.springframework.security.authenticationManager")
	private ProviderManager authenticationManager;
	@Autowired
	private GeoManager geoManager;
	
	public String showLoginPage() {
		if (getCurrentUser() != null)
			return "homePage";

		configUserLocale();
		return "loginPage";
	}
	public String showAccountHomePage() {
		if (getCurrentUser() != null)
			return "homePage";

		areaList = geoManager.findGeosByGeoTypeId("CHN_B");
		geo = geoManager.findGeoByIp(super.getRemoteIpAddress());
		return "accountHomePage";
	}
	public String showSignUpPage() {
		return "signUpPage";
	}
	public String signUp() {
		log.debug("signUp() method in class ProductAction is executed!");

		user.setIsEnabled(BaseEntity.FLAG_N);
		user.setCreatedDate(Calendar.getInstance().getTime());
		user.setPassword(md5PasswordEncoder.encodePassword(user.getPassword(),null));
		
		//Config the default roles for signed up user.
		Set<Role> roleSet = new LinkedHashSet<Role>();
		
		//set the sign up confirmation code
		user.setSignUpConfirmationCode(RandomStringGenerator.getNextSignUpConfirmationCode());
		
		userManager.saveOrUpdate(user);
		
		//Send a confirmation email
		mailManager.sendSignUpConfirmationMessage(user);
		return "signUpSuccess";
	}
	/**
	 * Activate user account according to the sign up confirmation code
	 * @return
	 */
	public String signUpConfirm() {
		//If it's the firt time to confirm 
		log.debug("signUpConfirm() method in class UserAction is executed!");
		String signUpConfirmCode = user.getSignUpConfirmationCode();
		user = userManager.getUser(user.getId());
		
		//If user is not exist, redirect to sign up page.
		if (user == null)
			return "showSignUpPage";
		
		//If user id and signUpConfirmId does not match, redirect to sign up page.
		if (!signUpConfirmCode.equals(user.getSignUpConfirmationCode()))
			return "showSignUpPage";
		
		//sign in automatically
		autoLoginWithNoPassword(user);
		//If Id has been confirmed before, redirect to home page.
		if (user.isEnabled()) {
			return "showHomePage";
		} else {
			//If Id has not been confirmed before, redirect to some welcome page and
			//send a welcome email and let the user do something ().
			
			if (user.getParty() == null) {
				Party userParty = new Party();
				user.setParty(userParty);
			}
			user.setIsEnabled(BaseEntity.FLAG_Y);
			userManager.saveOrUpdate(user);
			//mailManager.sendSignUpConfirmationMessage(user);
			return "wellcome";
		}
	}
	
	/**
	 * 登陆成功后首先执行此方法。
	 * 
	 * @return
	 */
	public String afterAcegi() {
		String username = super.getCurrentUserName();
		if (username != null) {
			log.debug("User with username [" + username
					+ "] has logined successfully!");
		}
		// config user locale
		configUserLocale();

		return SUCCESS;
	}
	
	private void autoLogin(String username, String password) {
		try {
			      // Must be called from request filtered by Spring Security, otherwise SecurityContextHolder is not updated
		  UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
		  token.setDetails(new WebAuthenticationDetails(request));
		  Authentication authentication = authenticationManager.authenticate(token);
		  Authentication auth = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
		  SecurityContextHolder.getContext().setAuthentication(authentication);
		} catch (Exception e) {
		  SecurityContextHolder.getContext().setAuthentication(null);
		  log.error("Failure in autoLogin");
		}
	}
	private void autoLoginWithNoPassword(User user) {
		try {
		  // Must be called from request filtered by Spring Security, otherwise SecurityContextHolder is not updated
		  Authentication auth = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
		  SecurityContextHolder.getContext().setAuthentication(auth);
		} catch (Exception e) {
		  SecurityContextHolder.getContext().setAuthentication(null);
		  log.error("Failure in autoLogin");
		}
	}
	/**
	 * 假如还没有登录，需要选用用户在本地设置的语言，如果未设置过则选用操作系统 的默认语言; 假如已经登录，则选用用户设置的语言；
	 */
	private void configUserLocale() {
		User user = getCurrentUser();
		String request_locale = null;
		String localeInCookie = getLocaleFromCookie();
		if (user != null) {
			if (user.getLastLocale() != null) // use last_locale firstly.
				request_locale = user.getLastLocale();
			else if (localeInCookie != null && !"".equals(localeInCookie))
				request_locale = localeInCookie;// else get cookie locale
												// secondly.
			// else use the system default.
		} else if (localeInCookie != null && !"".equals(localeInCookie))
			request_locale = localeInCookie;

		log.debug("The default request locale from client is ["
				+ request.getLocale() + "]");

		setLocaleInSessionCookie(request_locale);
	}

	private String getLocaleFromCookie() {
		Cookie[] cookies = this.getRequest().getCookies();
		String request_locale = null;
		for (Cookie cookie : cookies)
			if (ZuiConstants.LOCALE_COOKIE_NAME.equals(cookie.getName())) {
				request_locale = cookie.getValue();
				break;
			}
		return request_locale;
	}

	/**
	 * set locale to session, current action and cookie
	 * 
	 * @param locale
	 *            the string representation like "en_US_WIN"
	 */
	private void setLocaleInSessionCookie(String request_locale) {

		if (request_locale == null || "".equals(request_locale))
			request_locale = ZuiConstants.LOCALE_APPLICATION_DEFAULT;

		Locale locale = LocalizedTextUtil
				.localeFromString(request_locale, null);

		// sets local in session for action i18ninterceptor
		request.getSession().setAttribute(ZuiConstants.WW_TRANS_I18N_LOCALE,
				locale);

		// Sets the Locale for the current action
		ActionContext.getContext().setLocale(locale);

		// Sets the Locale for client cookie
		Cookie cookie = new Cookie(ZuiConstants.LOCALE_COOKIE_NAME,
				locale.toString());
		cookie.setMaxAge(365 * 24 * 60 * 60);
		response.addCookie(cookie);

		log.debug("The request locale [" + request_locale
				+ "] was stored in cookie and current session.");
	}

	public String showForgotPasswordPage() {
		return "forgotPassword";
	}	
	/**
	 * 发送找回密码链接的邮件
	 * 
	 * @return
	 */
	public String forgotPassword() {
		log.debug("forgotPassword() method in class ProductAction is executed!");
		
		user = userManager.findUniqueByProperty("email", user.getEmail());
		user.setResetPasswordToken(RandomStringGenerator.getNextResetPasswordToken());
		userManager.saveOrUpdate(user);
		
		//Send a reset password link email
		mailManager.sendResetPasswordMessage(user);
		
		return "showLoginPage";
	}
	/**
	 * 显示重设密码页面
	 * 
	 * @return
	 */
	public String showResetPasswordPage() {
		return "resetPassword";
	}	
	/**
	 * 重新设置密码
	 * 
	 * @return
	 */
	public String resetPassword() {
		//If it's the firt time to confirm 
		log.debug("resetPassword() method in class UserAction is executed!");
		String resetPasswordToken = user.getResetPasswordToken();
		String password = user.getPassword();
		user = userManager.getUser(user.getId());
		
		if (user != null && resetPasswordToken.equals(user.getResetPasswordToken())){
			//reset password
			user.setPassword(md5PasswordEncoder.encodePassword(password,null));
			userManager.saveOrUpdate(user);
			//sign in automatically
			autoLoginWithNoPassword(user);
			return "showHomePage";
		}

		return "showLoginPage";
	}
	/**
	 * 显示重发确认信页面
	 * 
	 * @return
	 */
	public String showResendConfirmationEmailPage() {
		return "resendConfirmationEmail";
	}	
	
	public String resendConfirmationEmail() {
		log.debug("resendConfirmationEmail() method in class ProductAction is executed!");
		
		user = userManager.findUniqueByProperty("email", user.getEmail());
		
		if (user==null) {
			//email is not exist.
			addActionError(getText("error.emailNotExist","This email was not found in our website - maybe you didn't signup yet?"));
			return "resendConfirmationEmail";
		}
		else if (user.isEnabled()) {
			//email has already been confirmed.
			addActionError(getText("error.emailConfirmed","Email has already been confirmed."));
			return "resendConfirmationEmail";
		} else {
			//Send a confirmation email
			mailManager.sendSignUpConfirmationMessage(user);
			return "signUpSuccess";
		}
	}

	/***********************************************************
	 * Business Method start
	 **********************************************************/
	public String index() {
		log.debug("index() method in class UserAction is executed!");
		return "index";
	}

	public String editNew() {
		log.debug("editNew() method in class UserAction is executed!");
		user = new User();
		return "editNew";
	}

	@SuppressWarnings("unchecked")
	public String edit() {
		if (user == null || user.getId() == null)
			return "editNew";

		user = (User) userManager.loadById(User.class, user.getId());
		return "edit";
	}

	public String view() {
		if (user.getId() == null)
			return "view";
		user = (User) userManager.loadById(User.class, user.getId());
		return "view";
	}

	public String delete() {
		if (jq.getId() == null)
			return "index";
		userManager.deleteEngtityFlagByIds(User.class, jq.getId().split("\\,"));
		/**
		 * @TODO: success message!
		 */
		return "index";
	}

	public String checkUniqueness() throws Exception {
		DetachedCriteria criteria = DetachedCriteria.forClass(User.class);
		if (checkedField != null && !"".equals(checkedField.trim())) {
			String methodName = "get"
					+ checkedField.substring(0, 1).toUpperCase()
					+ checkedField.substring(1);
			Method method = User.class.getMethod(methodName);
			criteria.add(Restrictions.eq(checkedField, method.invoke(user)));
			valid = !userManager.checkExistence(criteria);
		}
		return "jsonValid";
	}
	public String checkExistence() throws Exception {
		DetachedCriteria criteria = DetachedCriteria.forClass(User.class);
		if (checkedField != null && !"".equals(checkedField.trim())) {
			String methodName = "get"
					+ checkedField.substring(0, 1).toUpperCase()
					+ checkedField.substring(1);
			Method method = User.class.getMethod(methodName);
			criteria.add(Restrictions.eq(checkedField, method.invoke(user)));
			valid = userManager.checkExistence(criteria);
		}
		return "jsonValid";
	}
	public String create() {
		log.debug("create() method in class UserAction is executed!");
		if (user.getId() == null) {
			user.setCreatedByUserLogin(super.getCurrentUserName());
			user.setCreatedDate(Calendar.getInstance().getTime());
		}
		user.setLastModifiedByUserLogin(super.getCurrentUserName());
		user.setLastModifiedDate(Calendar.getInstance().getTime());

		user.setPassword(md5PasswordEncoder.encodePassword(user.getPassword(),
				null));
		userManager.saveOrUpdate(user);
		return "view";
	}

	public String createAndNew() {
		log.debug("createAndNew() method in class UserAction is executed!");
		this.create();
		user = null;
		return "editNew";
	}

	public String userJsonList() {
		DetachedCriteria criteria = DetachedCriteria.forClass(User.class);
		if (jq != null && jqFilters != null) {
			jq.setGroupCriteria(criteria, jqFilters);
		}
		criteria.add(Restrictions.eq("isDeleted", BaseEntity.FLAG_N));
		log.debug("userJsonList() method in class UserAction is executed!");
		userList = userManager.findByCriteriaForJQGrid(criteria,
				jq == null ? jq = new JqGridPage() : jq);

		// set null for session scope action
		jqFilters = null;
		return "userJsonList";
	}

	public void jqgridOper() {
		log.debug("jqgridOper() method in class UserAction is executed!");
		switch (jq.getOper()) {
		case del:
			delete();
			break;
		case add:
			create();
			break;
		case edit:
			create();
			break;
		default:
			break;
		}
	}

	public User getUser() {
		return user;
	}

	public void setUser(User user) {
		this.user = user;
	}

	public void setRoleList(List roleList) {
		this.roleList = roleList;
	}

	public List getUserList() {
		return userList;
	}

	public void setUserList(List userList) {
		this.userList = userList;
	}

	public List getSelectRoleList() {
		return selectRoleList;
	}

	public void setSelectRoleList(List selectRoleList) {
		this.selectRoleList = selectRoleList;
	}
	public Geo getGeo() {
		return geo;
	}
	public List<Geo> getAreaList() {
		return areaList;
	}
	public void setGeo(Geo geo) {
		this.geo = geo;
	}
	public void setAreaList(List<Geo> areaList) {
		this.areaList = areaList;
	}


}
